又再經歷了 10 天的遊戲專案設置,回顧這 10 天我們先設定了遊戲顯示大小、建構了背景、角色、移動邏輯及顯示優化、障礙以及狀態特效,是時候開始收斂整個專案將前述內容做統整及合併了!
那~就開始吧!
今天會使用到這 10 天完成的各個場景。
因為角色和背景都是白色的所以這裡先更新一下背景,區別一下。
現在我們讓我們的障礙物和背景結合。
ParallaxBackground
上的腳本。@export var obstacles:Array
3. 隨機在背景中加入障礙物。
宣告場景寬度並在初始化時取得,和之前取得高度的方法一樣。
var viewpoint_width:float
func _ready():
viewpoint_width = get_viewport().size.x
建立一個字典,鍵為背景,值為對應的障礙物陣列,之後我們可以透過背景去取得在那個節點下的障礙物們。
var backgrounds_to_obstacles:Dictionary
func _ready():
backgrounds_to_obstacles = {
background1: [],
background2: []
}
這裡我們在背景生成三種障礙物各一個並存到陣列中重複使用。
# func _ready 裡
# 從字典中取出所有的鍵,依序遞迴執行下述邏輯。
for bg in backgrounds_to_obstacles.keys():
# 在屬性面板設置的障礙物中依序遞迴執行下述邏輯。
for obstacle in obstacles:
# 生成障礙物
var obstacle_instantiate = obstacle.instantiate()
# 這裡因為不想要遊戲才剛開始就直接出現障礙物所以做了 work around:
# 生成在往上兩倍高的位置即代表不會被使用者看到的地方。
obstacle_instantiate.position = Vector2(0, -viewpoint_height*2)
# 加到 layer 節點下
bg.add_child(obstacle_instantiate)
# 加到字典中對應陣列的障礙物陣列中
backgrounds_to_obstacles[bg].append(obstacle_instantiate)
當遊戲執行時會產生下面的結構(括號及數字僅代表是不同實例)
|--ParallaxBackground
| |--DynamicBG2
| | |--Sprite2D
| | |--INVINCIBLE (2)
| | |--SLOW (2)
| | |--STOP (2)
| |
| |--DynamicBG1
| | |--Sprite2D
| | |--INVINCIBLE (1)
| | |--SLOW (1)
| | |--STOP (1)
而字典會如下(括號及數字僅代表是不同實例)
{
DynamicBG1 : [INVINCIBLE (1), SLOW (1), STOP (1)]
DynamicBG2 : [INVINCIBLE (2), SLOW (2), STOP (2)]
}
建立一個隨機取位置的方法,這裡我自己使用 add_child
到 ParallaxLayer
下座標 (0, 0)
會在畫面正中央,因此隨機在上下左右各一半的範圍隨機取值。
func get_randf_pos():
return Vector2(randf_range(-viewpoint_width/2, viewpoint_width/2), randf_range(-viewpoint_height/2, viewpoint_height/2))
最後更新我們之前建立的滾動背景邏輯。
func scroll_background(layer:ParallaxLayer):
# ...
# 當背景超過螢幕範圍時,
if offset.y >= viewpoint_height:
# ...
# 透過背景取得背景下的障礙物。
for obstacle in backgrounds_to_obstacles[layer]:
# 更新障礙物位置。
obstacle.position = get_randf_pos()
完整檔案
extends ParallaxBackground
@export var randomBackground:Array
@export var speed: float = 500
# obstacles
@export var obstacles:Array
var backgrounds_to_obstacles:Dictionary
var background1:ParallaxLayer
var background2:ParallaxLayer
var viewpoint_width:float
var viewpoint_height:float
# Called when the node enters the scene tree for the first time.
func _ready():
viewpoint_width = get_viewport().size.x
viewpoint_height = get_viewport().size.y
background1 = $DynamicBG1
background2 = $DynamicBG2
backgrounds_to_obstacles = {
background1: [],
background2: []
}
# create reused obstacles
for bg in backgrounds_to_obstacles.keys():
for obstacle in obstacles:
var obstacle_instantiate = obstacle.instantiate()
obstacle_instantiate.position = Vector2(0, -viewpoint_height*2)
bg.add_child(obstacle_instantiate)
backgrounds_to_obstacles[bg].append(obstacle_instantiate)
background2.set_motion_offset(Vector2(0, -viewpoint_height))
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
scroll_background(background1)
scroll_background(background2)
func scroll_background(layer:ParallaxLayer):
var offset = layer.get_motion_offset()
offset.y += speed * get_process_delta_time()
if offset.y >= viewpoint_height:
layer.get_node("Sprite2D")\
.set_texture(randomBackground[randi()%randomBackground.size()])
offset.y = offset.y - viewpoint_height*2
# set new randf pos for obstacles
for obstacle in backgrounds_to_obstacles[layer]:
obstacle.position = get_randf_pos()
layer.set_motion_offset(offset)
func get_randf_pos():
return Vector2(randf_range(-viewpoint_width/2, viewpoint_width/2), randf_range(-viewpoint_height/2, viewpoint_height/2))
:)